/*-------------------------------------------------------------------------
 *
 * relation.c
 *	  通用关系相关的例程。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/access/common/relation.c
 *
 * NOTES
 *	  此文件包含实现对关系（表、索引等）访问的 relation_ 例程。
 *	  特定于关系子类型的支持应放入其各自的文件中，而不是在这里。
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/relation.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/lmgr.h"
#include "utils/inval.h"
#include "utils/syscache.h"


/* ----------------
 *		relation_open - 通过关系 OID 打开任何关系
 *
 *		如果 lockmode 不是 "NoLock"，将获得指定类型的锁
 *		在关系上。 （通常，只有当调用者知道它已经在
 *		关系上有某种适当的锁时，才应该使用 NoLock。）
 *
 *		如果关系不存在，将引发错误。
 *
 *		注意：“关系”是指任何具有 pg_class 条目的内容。
 *		调用者需要检查 relkind 是否是它可以处理的内容。
 * ----------------
 */
Relation relation_open(Oid fc_relationId, LOCKMODE fc_lockmode)
{
	Relation	fc_r;

	Assert(fc_lockmode >= NoLock && fc_lockmode < MAX_LOCKMODES);

	/* 在尝试打开 relcache 条目之前获取锁 */
	if (fc_lockmode != NoLock)
		LockRelationOid(fc_relationId, fc_lockmode);

	/* relcache 执行所有实际工作... */
	fc_r = RelationIdGetRelation(fc_relationId);

	if (!RelationIsValid(fc_r))
		elog(ERROR, "could not open relation with OID %u", fc_relationId);

	/*
	 * 如果我们没有自己获得锁，断言调用者持有一个，
	 * 除非在引导模式下不使用任何锁。
	 */
	Assert(fc_lockmode != NoLock ||
		   IsBootstrapProcessingMode() ||
		   CheckRelationLockedByMe(fc_r, AccessShareLock, true));

	/* 记录我们已经访问了一个临时关系 */
	if (RelationUsesLocalBuffers(fc_r))
		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;

	pgstat_init_relation(fc_r);

	return fc_r;
}

/* ----------------
 *		try_relation_open - 通过关系 OID 打开任何关系
 *
 *		与 relation_open 相同，但是如果关系不存在，则返回 NULL 
 *		而不是失败。
 * ----------------
 */
Relation try_relation_open(Oid fc_relationId, LOCKMODE fc_lockmode)
{
	Relation	fc_r;

	Assert(fc_lockmode >= NoLock && fc_lockmode < MAX_LOCKMODES);

	/* 首先获取锁 */
	if (fc_lockmode != NoLock)
		LockRelationOid(fc_relationId, fc_lockmode);

	/*
	 * 现在我们已经获得了锁，探查关系是否真的存在
	 * 或者不存在。
	 */
	if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(fc_relationId)))
	{
		/* 释放无用的锁 */
		if (fc_lockmode != NoLock)
			UnlockRelationOid(fc_relationId, fc_lockmode);

		return NULL;
	}

	/* 做 relcache 加载应该是安全的 */
	fc_r = RelationIdGetRelation(fc_relationId);

	if (!RelationIsValid(fc_r))
		elog(ERROR, "could not open relation with OID %u", fc_relationId);

	/* 如果我们没有自己获得锁，断言调用者持有一个 */
	Assert(fc_lockmode != NoLock ||
		   CheckRelationLockedByMe(fc_r, AccessShareLock, true));

	/* 记录我们已经访问了一个临时关系 */
	if (RelationUsesLocalBuffers(fc_r))
		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;

	pgstat_init_relation(fc_r);

	return fc_r;
}

/* ----------------
 *		relation_openrv - 通过 RangeVar 打开任何关系
 *
 *		与 relation_open 相同，但关系是通过 RangeVar 指定的。
 * ----------------
 */
Relation relation_openrv(const RangeVar *fc_relation, LOCKMODE fc_lockmode)
{
	Oid			fc_relOid;

	/*
	 * 在尝试打开关系之前检查共享缓存无效消息。
	 * 即使我们已经在关系上持有一个锁，这也很重要，
	 * 因为 GRANT/REVOKE 在不对目标关系加锁的情况下执行，
	 * 我们想确保看到当前的 ACL 信息。如果请求了 NoLock，
	 * 则可以跳过此步骤，假定这样的调用不是当前命令中的第一个，
	 * 因此我们应该已经相对最新。 （XXX 这一切都可以重新设计，
	 * 但就目前而言，我们会继续像历史上那样做。）
	 */
	if (fc_lockmode != NoLock)
		AcceptInvalidationMessages();

	/* 查找并使用命名空间搜索锁定适当的关系 */
	fc_relOid = RangeVarGetRelid(fc_relation, fc_lockmode, false);

	/* 让 relation_open 来处理其余部分 */
	return relation_open(fc_relOid, NoLock);
}

/* ----------------
 *		relation_openrv_extended - 通过 RangeVar 打开任何关系
 *
 *		与 relation_openrv 相同，但具有一个额外的 missing_ok 参数
 *		允许在未找到关系时返回 NULL，而不是错误。 
 *		（请注意，一些其他原因，例如权限问题，
 *		仍将导致 ereport。）
 * ----------------
 */
Relation relation_openrv_extended(const RangeVar *fc_relation, LOCKMODE fc_lockmode,
						 bool fc_missing_ok)
{
	Oid			fc_relOid;

	/*
	 * 在尝试打开关系之前检查共享缓存无效消息。
	 * 请参见 relation_openrv() 中的注释。
	 */
	if (fc_lockmode != NoLock)
		AcceptInvalidationMessages();

	/* 查找并使用命名空间搜索锁定适当的关系 */
	fc_relOid = RangeVarGetRelid(fc_relation, fc_lockmode, fc_missing_ok);

	/* 未找到时返回 NULL */
	if (!OidIsValid(fc_relOid))
		return NULL;

	/* 让 relation_open 来处理其余部分 */
	return relation_open(fc_relOid, NoLock);
}

/* ----------------
 *		relation_close - 关闭任何关系
 *
 *		如果 lockmode 不是 "NoLock"，那么我们就释放指定的锁。
 *
 *		请注意，通常在 relation_close 之后保持锁是合理的；
 *		在这种情况下，锁将在事务结束时自动释放。
 * ----------------
 */
void relation_close(Relation fc_relation, LOCKMODE fc_lockmode)
{
	LockRelId	fc_relid = fc_relation->rd_lockInfo.lockRelId;

	Assert(fc_lockmode >= NoLock && fc_lockmode < MAX_LOCKMODES);

	/* 关系缓存执行实际工作... */
	RelationClose(fc_relation);

	if (fc_lockmode != NoLock)
		UnlockRelationId(&fc_relid, fc_lockmode);
}
